package logic

import (
	"bytes"
	"embed"
	"fmt"
	"io"
	"nodemessage.com/codetool/util/fileutil"
	"nodemessage.com/codetool/util/strutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

var (
	//go:embed vm/*.vm
	vm                  embed.FS
	daoTemplate         *template.Template
	entityTemplate      *template.Template
	serviceTemplate     *template.Template
	serviceimplTemplate *template.Template
	controllerTemplate  *template.Template
)

func init() {
	daoTemplate, _ = template.ParseFS(vm, "vm/dao.java.vm")
	entityTemplate, _ = template.ParseFS(vm, "vm/entity.java.vm")
	serviceTemplate, _ = template.ParseFS(vm, "vm/service.java.vm")
	serviceimplTemplate, _ = template.ParseFS(vm, "vm/serviceimpl.java.vm")
	controllerTemplate, _ = template.ParseFS(vm, "vm/controller.java.vm")
}

func GetVmFs(path string) (*template.Template, error) {
	return template.ParseFS(vm, path)
}

func DaoTypeToType(dbType string) string {
	dbType = strings.ToLower(dbType)
	if strings.Index(dbType, "int") > -1 ||
		strings.Index(dbType, "tinyint") > -1 {
		return "Integer"
	}
	if strings.Index(dbType, "bigint") > -1 {
		return "Long"
	}
	if strings.Index(dbType, "decimal") > -1 {
		return "BigDecimal"
	}
	if strings.Index(dbType, "varchar") > -1 ||
		strings.Index(dbType, "char") > -1 {
		return "String"
	}
	if strings.Index(dbType, "datetime") > -1 ||
		strings.Index(dbType, "date") > -1 {
		return "Date"
	}
	return ""
}

type JavaMeta struct {
	/* 包信息 */
	ProjectName     string //项目名
	BasePackage     string `mapstructure:"basePackage"` // 根包
	Author          string `mapstructure:"author"`      // 作者
	BasePath        string `mapstructure:"basePath"`    // 项目根路径
	basePackagePath string
	Entity          MetaGen `mapstructure:"entity"`
	Dao             MetaGen `mapstructure:"dao"`
	Service         MetaGen `mapstructure:"service"`
	ServiceImpl     MetaGen `mapstructure:"serviceImpl"`
	Controller      MetaGen `mapstructure:"controller"`
}
type MetaGen struct {
	Gen       bool   `mapstructure:"gen"`
	Overwrite bool   `mapstructure:"overwrite"`
	Package   string // 相对与根包的实体类包名
	Suffix    string // 实体后缀
}

type JavaBase struct {
	Package     string // 相对与根包的实体类包名
	Name        string // 实体名
	Suffix      string // 实体后缀
	Comment     string // 实体备注
	PackagePath string
}

type JavaEntity struct {
	JavaBase
	TableName string      // 表名
	Field     []JavaField // 字段
}

type JavaField struct {
	Name    string
	Type    string
	Comment string
}

type JavaDao struct {
	JavaBase
}

type JavaService struct {
	JavaBase
}

type JavaServiceImpl struct {
}

type JavaController struct {
	JavaBase
	ModelUrl string
}

// JavaGenBuilder 针对于 JavaGen 的 Builder.
type JavaGenBuilder struct {
	JavaMeta
	Entity      JavaEntity
	Dao         JavaDao
	Service     JavaService
	ServiceImpl JavaServiceImpl
	Controller  JavaController
}

func BuilderJavaGen(meta JavaMeta) *JavaGenBuilder {
	return &JavaGenBuilder{
		JavaMeta: meta,
	}
}

func (g *JavaGenBuilder) BuildJavaEntity(entity JavaEntity) *JavaGenBuilder {
	g.Entity = entity
	return g
}

func (g *JavaGenBuilder) BuildJavaDao(dao JavaDao) *JavaGenBuilder {
	g.Dao = dao
	return g
}

func (g *JavaGenBuilder) BuildJavaService(service JavaService) *JavaGenBuilder {
	g.Service = service
	return g
}

func (g *JavaGenBuilder) BuildJavaServiceImpl(serviceImpl JavaServiceImpl) *JavaGenBuilder {
	g.ServiceImpl = serviceImpl
	return g
}

func (g *JavaGenBuilder) BuildJavaController(controller JavaController) *JavaGenBuilder {
	g.Controller = controller
	return g
}

func (g *JavaGenBuilder) Build() *JavaGen {
	gen := JavaGen{
		JavaMeta:    g.JavaMeta,
		Entity:      &g.Entity,
		Dao:         &g.Dao,
		Service:     &g.Service,
		ServiceImpl: &g.ServiceImpl,
		Controller:  &g.Controller,
	}
	if err := gen.genPath(); err != nil {
		panic(err)
	}
	return &gen
}

// JavaGen 代码生成基础工具
type JavaGen struct {
	JavaMeta
	Entity      *JavaEntity
	Dao         *JavaDao
	Service     *JavaService
	ServiceImpl *JavaServiceImpl
	Controller  *JavaController
}

func (g *JavaGen) gen(exec func(wr io.Writer, data any) error) (result []byte, err error) {
	if g.BasePackage == "" {
		// 校验必填
		return nil, fmt.Errorf("BasePackage 未填写")
	}
	buffer := bytes.Buffer{}
	err = exec(&buffer, g)
	if err == nil {
		result = buffer.Bytes()
	}
	return
}

func (g *JavaGen) genFile(genExe func() ([]byte, error), fileBase JavaBase, overwrite bool) (err error) {
	// 参数校验
	if fileBase.PackagePath == "" {
		err = fmt.Errorf("PackagePath为空")
		return
	}
	content, err := genExe()
	if err != nil {
		return
	}
	if len(content) == 0 {
		err = fmt.Errorf("内容为空")
		fmt.Println(strutil.Error + path.Join(fileBase.PackagePath, fileBase.Name+fileBase.Suffix) + ".java 解析内容为空")
		return
	}
	// 文件校验
	fileName := fileBase.Name + fileBase.Suffix + ".java"
	filePath := filepath.Join(fileBase.PackagePath, fileName)
	_, err = os.Stat(filePath)
	if err == nil && !overwrite {
		fmt.Println(strutil.Waring + path.Join(fileBase.PackagePath, fileBase.Name+fileBase.Suffix) + ".java 文件已存在")
		return
	}
	// 内容写入
	fileInstance, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm)
	if err != nil {
		fmt.Println(strutil.Error + path.Join(fileBase.PackagePath, fileBase.Name+fileBase.Suffix) + ".java 文件打开失败")
		return
	}
	defer fileInstance.Close()
	_, err = fileInstance.Write(content)
	if err == nil {
		fmt.Println(strutil.Info + path.Join(fileBase.PackagePath, fileBase.Name+fileBase.Suffix) + ".java 创建成功")
	}
	return
}

func (g *JavaGen) GenEntity(overwrite bool) error {
	if g.Entity == nil {
		return fmt.Errorf("entity未构建")
	}
	return g.genFile(func() ([]byte, error) {
		return g.gen(entityTemplate.Execute)
	}, g.Entity.JavaBase, overwrite)
}

func (g *JavaGen) GenDao(overwrite bool) error {
	if g.Dao == nil {
		return fmt.Errorf("dao未构建")
	}
	return g.genFile(func() ([]byte, error) {
		return g.gen(daoTemplate.Execute)
	}, g.Dao.JavaBase, overwrite)
}

func (g *JavaGen) GenService(overwrite bool) error {
	if g.Service == nil {
		return fmt.Errorf("service未构建")
	}
	return g.genFile(func() ([]byte, error) {
		return g.gen(serviceTemplate.Execute)
	}, g.Service.JavaBase, overwrite)
}

func (g *JavaGen) GenServiceImpl(overwrite bool) error {
	if g.Service == nil {
		return fmt.Errorf("service未构建")
	}
	serviceBase := g.Service.JavaBase
	return g.genFile(func() ([]byte, error) {
		return g.gen(serviceimplTemplate.Execute)
	}, JavaBase{
		PackagePath: path.Join(serviceBase.PackagePath, "impl"),
		Name:        serviceBase.Name,
		Suffix:      serviceBase.Suffix + "Impl",
	}, overwrite)
}

func (g *JavaGen) GenController(overwrite bool) error {
	if g.Controller == nil {
		return fmt.Errorf("controller未构建")
	}
	return g.genFile(func() ([]byte, error) {
		return g.gen(controllerTemplate.Execute)
	}, g.Controller.JavaBase, overwrite)
}

func (g *JavaGen) genPath() (err error) {
	if g.BasePath == "" {
		// 校验必填
		err = fmt.Errorf("BasePackagePath 未填写")
		return
	}
	if g.BasePackage == "" {
		// 校验必填
		err = fmt.Errorf("BasePackage 未填写")
		return
	}
	g.basePackagePath = g.BasePath
	for _, s := range strings.Split(g.BasePackage, ".") {
		g.basePackagePath = path.Join(g.basePackagePath, s)
	}
	if err = fileutil.MkdirPath(g.basePackagePath); err != nil {
		return
	}
	if g.Entity != nil && g.Entity.Package != "" {
		// entity
		g.Entity.PackagePath = g.basePackagePath
		for _, s := range strings.Split(g.Entity.Package, ".") {
			g.Entity.PackagePath = path.Join(g.Entity.PackagePath, s)
		}
		if err = fileutil.MkdirPath(g.Entity.PackagePath); err != nil {
			return
		}
	}
	if g.Dao != nil && g.Dao.Package != "" {
		// dao
		g.Dao.PackagePath = g.basePackagePath
		for _, s := range strings.Split(g.Dao.Package, ".") {
			g.Dao.PackagePath = path.Join(g.Dao.PackagePath, s)
		}
		if err = fileutil.MkdirPath(g.Dao.PackagePath); err != nil {
			return
		}
	}
	if g.Service != nil && g.Service.Package != "" {
		// service
		g.Service.PackagePath = g.basePackagePath
		for _, s := range strings.Split(g.Service.Package, ".") {
			g.Service.PackagePath = path.Join(g.Service.PackagePath, s)
		}
		if err = fileutil.MkdirPath(g.Service.PackagePath); err != nil {
			return
		}
		// serviceImpl
		if err = fileutil.MkdirPath(path.Join(g.Service.PackagePath, "impl")); err != nil {
			return
		}
	}
	if g.Controller != nil && g.Controller.Package != "" {
		// controller
		g.Controller.PackagePath = g.basePackagePath
		for _, s := range strings.Split(g.Controller.Package, ".") {
			g.Controller.PackagePath = path.Join(g.Controller.PackagePath, s)
		}
		if err = fileutil.MkdirPath(g.Controller.PackagePath); err != nil {
			return
		}
	}
	// base包构建
	if err = fileutil.MkdirPath(path.Join(g.basePackagePath, "base")); err != nil {
		return
	} else {
		for _, s := range []string{"BaseController.java", "BaseReqType.java", "BaseResultEntity.java", "RequestUtils.java", "ResultStatus.java"} {
			templatePath := path.Join(g.basePackagePath, "base", s)
			if !fileutil.Exist(templatePath) {
				if templateVm, err := GetVmFs("vm/" + s + ".vm"); err != nil {
					panic(err)
				} else {
					buffer := bytes.Buffer{}
					err = templateVm.Execute(&buffer, g)
					if err = fileutil.WriteToFile(templatePath, buffer.Bytes()); err != nil {
						panic(err)
					}
				}
			}
		}
	}
	// 启动类构建
	if strapMainTemplate, err := GetVmFs("vm/StrapMain.java.vm"); err != nil {
		panic(err)
	} else {
		strapMainPath := path.Join(g.basePackagePath, "StrapMain.java")
		buffer := bytes.Buffer{}
		err = strapMainTemplate.Execute(&buffer, g)
		if err = fileutil.WriteToFile(strapMainPath, buffer.Bytes()); err != nil {
			panic(err)
		}
	}

	return
}

func (g *JavaGen) FirstToLower(data string) string {
	return strutil.FirstToLower(data)
}

func (g *JavaGen) SnakeToCamelCase(data string) string {
	return strutil.SnakeToCamelCase(data)
}
